package com.prs.crm.service.flow.impl;

import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import com.prs.crm.context.Constrains;
import com.prs.crm.dao.flow.WorkFlowDao;
import com.prs.crm.domain.flow.WorkFlow;
import com.prs.crm.domain.flow.WorkFlowNode;
import com.prs.crm.domain.flow.WorkFlowTransition;
import com.prs.crm.flow.adapter.IFlowXmlReader;
import com.prs.crm.flow.adapter.IFlowXmlWriter;
import com.prs.crm.service.flow.WorkFlowInstanceService;
import com.prs.crm.service.flow.WorkFlowService;
import com.prs.crm.service.impl.BaseServiceImpl;

public class WorkFlowServiceImpl extends BaseServiceImpl<WorkFlow,WorkFlowDao> implements WorkFlowService{
	
	private WorkFlowInstanceService workflowInstanceService;
	private IFlowXmlReader flowXmlReader;
	private IFlowXmlWriter flowXmlWriter;
	
	public void setFlowXmlReader(IFlowXmlReader flowXmlReader) {
		this.flowXmlReader = flowXmlReader;
	}

	public IFlowXmlReader getFlowXmlReader() {
		return flowXmlReader;
	}

	public void setFlowXmlWriter(IFlowXmlWriter flowXmlWriter) {
		this.flowXmlWriter = flowXmlWriter;
	}

	public IFlowXmlWriter getFlowXmlWriter() {
		return flowXmlWriter;
	}
	
	
	public void setWorkflowInstanceService(WorkFlowInstanceService workflowInstanceService) {
		this.workflowInstanceService = workflowInstanceService;
	}

	public WorkFlowInstanceService getWorkflowInstanceService() {
		return workflowInstanceService;
	}
	
	public boolean removeFlow(WorkFlow flow)
	{
		if (workflowInstanceService.countFlowInstance(flow)>0)
			return false;
		super.remove(flow);
		return true;
	}
	
	@Override
	public void save(WorkFlow flow)
	{
		if (flow.getState()==null)
			flow.setState(Constrains.WORKFLOWSTATE_DISABLE);
		super.save(flow);
	}
	
	public void  executeEnable(WorkFlow workflow)
	{
		workflow.setState(Constrains.WORKFLOWSTATE_ENABLE);
		super.save(workflow);
	}
	
	public void executeDisable(WorkFlow workflow)
	{
		workflow.setState(Constrains.WORKFLOWSTATE_DISABLE);
		super.save(workflow);
	}
	
	public 	String getDesginXml(WorkFlow flow)
	{
		StringWriter output = new StringWriter();
		getFlowXmlWriter().write(flow,output);
		return output.toString();
	}
	
	
	public void saveDesgin(WorkFlow flow,String xml)
	{
		List<WorkFlowTransition> oldTrans = new ArrayList<WorkFlowTransition>();
		oldTrans.addAll(flow.getTransitions());
		
		Collection<WorkFlowNode> oldNodes = new ArrayList<WorkFlowNode>();
		oldNodes.addAll(flow.getNodes());
		
		Set<WorkFlowNode> nodeset = new HashSet<WorkFlowNode>();
		getFlowXmlReader().read(xml);
		Map<Integer,WorkFlowNode> cache = new HashMap<Integer,WorkFlowNode>();
		WorkFlowNode existnode = null;
		Iterator<WorkFlowNode> iterator = getFlowXmlReader().getNodes().iterator();
		while (iterator.hasNext())
		{
			WorkFlowNode node = iterator.next();
			
			if (node.getId()!=null)
			{
				
				existnode = this.getDao().exists(flow,node);
				
				if (existnode!=null && existnode.getFlow()!=null
						&& existnode.getFlow().getId().equals(flow.getId()))
				{
					
					existnode.setName(node.getName());
					existnode.setType(node.getType());
					existnode.setSequence(node.getSequence());
					existnode.getExtra().setXcoordinate(node.getExtra().getXcoordinate());
					existnode.getExtra().setYcoordinate(node.getExtra().getYcoordinate());
					existnode.getExtra().setWidth(node.getExtra().getWidth());
					existnode.getExtra().setHeight(node.getExtra().getHeight());
					cache.put(existnode.getId(), existnode);
					node = existnode;
				}
				else
					node.setId(null);
				
				
			}
			if (node.getFlow()==null)
				node.setFlow(flow);
			//this.getDao().saveNode(node);
			if (node.getExtra()!=null && node.getExtra().getNode()==null)
				node.getExtra().setNode(node);
			oldNodes.remove(node);
			nodeset.add(node);
			this.getDao().saveFlowNode(node);
		
			existnode = null;
		}
		
		
		Set<WorkFlowTransition> transet = new HashSet<WorkFlowTransition>();
		WorkFlowTransition existran = null;

		Iterator<WorkFlowTransition> titerator = getFlowXmlReader().getTransitions().iterator();
		while (titerator.hasNext())
		{
			WorkFlowTransition trans = titerator.next();

			if (cache.containsKey(trans.getStartnode().getId()))
				trans.setStartnode(cache.get(trans.getStartnode().getId()));
			if (cache.containsKey(trans.getEndnode().getId()))
				trans.setEndnode(cache.get(trans.getEndnode().getId()));
		
			
			if (trans.getFlow()==null)
				trans.setFlow(flow);
		
			existran = this.getDao().exists(flow,trans);
			if (existran!=null)
			{
				existran.setName(trans.getName());
				existran.setStartnode(trans.getStartnode());
				existran.setEndnode(trans.getEndnode());
				
				trans = existran;
			}
			else
			{
				trans.setId(null);
			}
			
			if (oldTrans.contains(trans))
			{
				oldTrans.remove(trans);
			}
			transet.add(trans);
		}
		flow.getNodes().clear();
		flow.getTransitions().clear();
		flow.getNodes().addAll(nodeset);
		flow.getTransitions().addAll(transet);
		this.save(flow);
		if (oldNodes.size()>0)
		{
			for (WorkFlowNode oldnode : oldNodes)
			{
				Collection<WorkFlowTransition> drafttrans = new ArrayList<WorkFlowTransition>();
				for (WorkFlowTransition trans : flow.getTransitions())
				{
					if (trans.getStartnode().equals(oldnode)
							|| trans.getEndnode().equals(oldnode))
					{
						drafttrans.add(trans);
					}
				}
				for (WorkFlowTransition draft : drafttrans)
				{
					flow.getTransitions().remove(draft);
					this.getDao().removeTransition(draft);
				}
				flow.getNodes().remove(oldnode);
				this.getDao().removeNode(oldnode);
			}
		}
		if (oldTrans.size()>0)
		{
	
			for (WorkFlowTransition trans : oldTrans)
			{
				this.getDao().removeTransition(trans);
			}
		}
	}
	
	public Collection<WorkFlow> getEnableFlows()
	{
		return this.getDao().findByState(Constrains.WORKFLOWSTATE_ENABLE);
	}

	@Override
	public List<WorkFlow> getWorkFlow(String type) {
		return this.getDao().getWorkFlow(type);
	}

	@Override
	public List<WorkFlow> getAllWorkFlow() {
		return this.getDao().getAllWorkFlow();
	}
	


}
